//---------------------------------------------------------------------------------
// Copyright (c) 2011, Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//---------------------------------------------------------------------------------

namespace Microsoft.ServiceBus.Samples.ClientAccessPolicyPublisher
{
    using System;
    using System.Collections.Specialized;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;

    public class Program
    {
        private static string serviceNamespace;
        private static string issuerName;
        private static string issuerSecret;

        private static string accessToken;
        private static string clientAccessPolicyUri;

        static void Main(string[] args)
        {
            Console.WriteLine("Windows Azure Service Bus Client Access Policy Publishing Utility");
            Console.WriteLine("Copyright (c) Microsoft Corporation. All rights reserved.");
            Console.WriteLine();

            serviceNamespace = Prompt("Service Namespace (eg: contoso)");
            issuerName = Prompt("Issuer Name", isRequired: false, defaultValue: "owner");
            issuerSecret = Prompt("Issuer Secret");
            clientAccessPolicyUri = "https://" + serviceNamespace + ".servicebus.windows.net/clientaccesspolicy.xml";
            Console.WriteLine();

            try
            {
                Console.Write("Requesting a token from the Access Control Service...");
                accessToken = GetAcsToken();
                Console.WriteLine("[OK]");
            }
            catch (Exception e)
            {
                Error(e);
            }

            Console.WriteLine();
            try
            {
                Console.WriteLine("What do you want to do?");
                Console.WriteLine("    Get the current client access policy      [G]");
                Console.WriteLine("    Put a new client access policy            [P]");
                Console.WriteLine("    Delete the current client access policy   [D]");
                Console.WriteLine("    Exit this tool                            [X]");

                char choice = default(char);
                while (choice != 'X')
                {
                    Console.WriteLine();
                    choice = Choice("Choose an option", 'G', 'P', 'D', 'X');

                    switch (choice)
                    {
                        case 'G':
                            GetPolicyFile();
                            break;
                        case 'P':
                            PutPolicyFile();
                            break;
                        case 'D':
                            DeletePolicyFile();
                            break;
                    }
                }

            }
            catch (Exception e)
            {
                Error(e);
            }
        }

        private static string GetAcsToken()
        {
            var acsEndpoint = "https://" + serviceNamespace + "-sb.accesscontrol.windows.net/WRAPv0.9/";
            var realm = "http://" + serviceNamespace + ".servicebus.windows.net";

            var data = new NameValueCollection()
            {
                { "wrap_name", issuerName },
                { "wrap_password", issuerSecret },
                { "wrap_scope", realm }
            };

            var webClient = new WebClient();
            var responseBuffer = webClient.UploadValues(acsEndpoint, "POST", data);

            var response = Encoding.UTF8.GetString(responseBuffer);
            var responseProperties = response.Split('&');
            var tokenProperty = responseProperties[0].Split('=');
            var token = Uri.UnescapeDataString(tokenProperty[1]);

            return "WRAP access_token=\"" + token + "\"";
        }

        private static void GetPolicyFile()
        {
            var webClient = new WebClient();
            try
            {
                var result = webClient.DownloadString(clientAccessPolicyUri);
                ColorConsole.WriteLine(ConsoleColor.Cyan, result);

                var shouldSave = Choice("Save To Disk", 'Y', 'N');
                if (shouldSave == 'Y')
                {
                    var fileName = Prompt("Filename");
                    File.WriteAllText(fileName, result);
                    ColorConsole.WriteLine(ConsoleColor.Green, "Saved Successfully.");
                }
            }
            catch (WebException e)
            {
                var response = e.Response as HttpWebResponse;
                if (response != null && response.StatusCode == HttpStatusCode.NotFound)
                {
                    ColorConsole.WriteLine(ConsoleColor.Yellow, "No Policy Exists");
                }
                else
                {
                    throw;
                }
            }
        }

        private static void PutPolicyFile()
        {
            string filePath = null;
            while (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
            {
                var fileName = Prompt("Filename to upload", isRequired: false, defaultValue: "ClientAccessPolicy.xml");
                filePath = Path.GetFullPath(fileName);
                if (!File.Exists(filePath))
                {
                    ColorConsole.WriteLine(ConsoleColor.Red, "Specifed file does not exist (Path: {0})", filePath);
                }
            }

            var webClient = new WebClient();
            webClient.Headers[HttpRequestHeader.Authorization] = accessToken;
            webClient.Headers[HttpRequestHeader.ContentType] = "text/xml";

            webClient.UploadFile(clientAccessPolicyUri, "PUT", filePath);
            ColorConsole.WriteLine(ConsoleColor.Green, "Uploaded Successfully.");

        }

        private static void DeletePolicyFile()
        {
            var confirm = Choice("Are You Sure?", 'Y', 'N');
            if (confirm == 'Y')
            {
                var webClient = new WebClient();
                webClient.Headers[HttpRequestHeader.Authorization] = accessToken;
                webClient.UploadString(clientAccessPolicyUri, "DELETE", string.Empty);
                ColorConsole.WriteLine(ConsoleColor.Green, "Deleted Successfully.");
            }
        }

        private static string Prompt(string prompt, bool isRequired = true, string defaultValue = null, string validationMessage = "Please enter a value")
        {
            string result = null;
            if (defaultValue != null)
            {
                prompt += " [Default: '" + defaultValue + "']";
            }

            do
            {
                Console.Write(prompt + ": ");
                result = Console.ReadLine();

                if (isRequired && string.IsNullOrWhiteSpace(result))
                {
                    ColorConsole.WriteLine(ConsoleColor.Red, "Error: {0}", validationMessage);
                }
            } while (isRequired && string.IsNullOrWhiteSpace(result));

            if (!isRequired && string.IsNullOrWhiteSpace(result))
            {
                result = defaultValue;
            }

            return result;
        }

        private static char Choice(string prompt, params char[] options)
        {
            string result = null;
            prompt = string.Format("{0} [{1}]: ", prompt, string.Join(", ", options));

            do
            {
                Console.Write(prompt);
                result = Console.ReadLine();

            } while (result.Length != 1 || !options.Any(o => char.ToUpper(o) == char.ToUpper(result[0])));

            return char.ToUpper(result[0]);
        }

        private static void Error(Exception e)
        {
            ColorConsole.WriteLine(ConsoleColor.Red, "[FAIL]");
            ColorConsole.WriteLine(ConsoleColor.Red, "Error: " + e.Message);

            Console.WriteLine();
            Console.Write("Press <Enter> to Exit");
            Console.ReadKey();
            Environment.Exit(-1);
        }
    }
}
